#include "head.h"
#include "cc9d_support.h"
#include "adc.h"
#include "App_adc.h"
#include <stdio.h>





//改版记录 - V2
//1.增加远程控制水泵的能力。

#define LOCAL_PORT AdcApp_PORT

static uint32 sec_ticks = 0;//1s计数
static uint16 remote_addr = 0x0000; //远程地址
static uint8 to_port = 32; //目标端口
static uint16 adc_report_threshold = 16;//ADC值变化的阈值，超过此值则发送一个包报告ADC值。



static uint16 Adc_interval = 60;//ADC报告的时间间隔，
static uint16 Hart_beat_interval = 60 * 30;//心跳包的时间间隔，
static uint16 Store_interval = 60 * 5;//储存ADC值的时间间隔，


//设置为快速模式，用于调试
void setFastMode(sCc9dPkg* recv_pkg, uint8 data_len) {
	//根据来包的参数设置用于调试的快速模式
	//0007.32:/f /f
	if (data_len != 2) {
		responePkg(recv_pkg, BAD_LENGTH);
		return;
	}
	uint8* arguments = (void*)(recv_pkg->datas + 1);
	if (arguments[0] == 'f') {
		Adc_interval = 2;
		Hart_beat_interval = 2;
		Store_interval = 2;
		responePkg(recv_pkg, 'f');
		
	} else {
		Adc_interval = 60;
		Hart_beat_interval = 60 * 30;
		Store_interval = 60 * 5;
		responePkg(recv_pkg, 'n');
	}
}


//24小时 = 24*60 = 1440分钟，保证能够储存24小时的数据，所以每2分钟储存一个数据。



//储存ADC值到RAM中的结构体，每10分钟储存一个
typedef struct {
    uint16 adc_value;
    uint16 sno;
}sAdcRecord;
#define RECORD_LEN 2048
static sAdcRecord adc_records[RECORD_LEN];
//储存ADC值的索引，每储存一个值，索引加1，到索引上限后再次循环。
static int adc_record_index = 0;
//最后一次记录ADC的时间ms值，
static int last_store_sec_ticks = 0;
//记录的序号，每次记录加1，到上限后再次循环。
static uint16 record_sno = 0;
static void storeAdc(uint16 adc_value){
	last_store_sec_ticks = sec_ticks;
    adc_records[adc_record_index].adc_value = adc_value;
    adc_records[adc_record_index].sno = record_sno;
    adc_record_index++;
    adc_record_index %= RECORD_LEN;
    record_sno++;
    printf("T= %lds, store adc %x, sno %d\n", sec_ticks, adc_value, record_sno);
}
//获取某个序号对应的索引
static int getRecord(uint16 target){
    int distance = record_sno - target;//通常target较小，所以distance为正数
    //如果record_sno可能溢出，既0xffff+1 = 0,
    //这种情况会造成distance为负数，所以需要修正
    if (distance < 0) {
        distance += 0x10000;
    }
    if(distance >= RECORD_LEN){
        return -1;
    }
    int index = adc_record_index - distance;
    if (index < 0){
        index += RECORD_LEN;
    }    
    if (adc_records[index].sno != target){
        printf("getRecord error, target %d, index %d, sno %d\n", target, index, adc_records[index].sno);
        return -1;
    }else{
        return index;
    }
}

static void getHistory(sCc9dPkg* recv_pkg, uint8 data_len) {    //获取历史数据
    //第一步，解析参数
    struct {//将收到的数据按照结构体理解
        uint16 sno;//从哪个SNO开始
        uint8 num;//要获取多少个数据，负数表示从sno往前数
    }*arguments = (void*)(recv_pkg->datas + 1);
    uint16 sno;
    int num;
    if (data_len == 4) {
        sno = arguments->sno;
        num = arguments->num;
        if (num > 30) {
            num = 30;
        }
    } else if (data_len == 3) {
        sno = arguments->sno;
        num = 0;
    } else {
        printf("get data error, data_len = %d\n", data_len);
        return;
    }
    printf("get sno %d, num %d\n", sno, num);

    //第二步，准备发送包
    sCc9dPkg send_pkg;
    int send_len = 0;
    createResponsePkg(&send_pkg, recv_pkg);

    //第三步，填充数据的SNO
    *((uint16*)(send_pkg.datas)) = arguments->sno;//第一组数据是SNO
    send_len += 2;
    //第四步，填充后续的数据
    int count = 0;
    int index = getRecord(arguments->sno);
    while (index != -1) {
        ((uint16*)(send_pkg.datas + 2))[count] = adc_records[index].adc_value;
        send_len += 2;
        sno--;
        count++;
        if (count >= num) {
            break;
        }
        index = getRecord(sno);
    }
    //第五步，发送包
    cc9d_SendPkg(&send_pkg, send_len);
}




static void sendAdcPkg(uint16 adc_value) {
    sCc9dPkg send_pkg;
    send_pkg.from_port = LOCAL_PORT;
    send_pkg.to_port = to_port;
    send_pkg.remote_addr = remote_addr;
    send_pkg.datas[0] = (adc_value >> 0) & 0xff;
    send_pkg.datas[1] = (adc_value >> 8) & 0xff;
    cc9d_SendPkg(&send_pkg, 2);
}


//--------------关于水泵的可配置参数-------------------------------
	static uint16 low_threshold = 0;//需要灌溉的最低阈值
	static uint16 wait_time = 1800;//启动灌溉后的等待时间，30min
	static uint8 pumb_run_seconds = 2;//每次水泵运行的时间，单位秒

	static uint16 pumb_node_addr = 0x0000;//水泵节点地址
	static uint8 pumb_port = 48;//水泵端口
//---------------------------------------------------------------

static const uint8 pumb_cmd = 'o';//水泵命令
static uint32 last_run_time = 0;//上一次打开水泵的时刻

//每次读取ADC值后，根据阈值判断是否需要灌溉
static void pumbControl(uint16 adc_value) {
	//如果距离上一次灌溉的时间小于等待时间，则不进行灌溉
	if(sec_ticks - last_run_time < wait_time){
		return;
	}
	//如果ADC值小于阈值
	//针对模块的土壤湿度传感器，湿度越小，ADC越大，这里改了比较方向
	if(adc_value > low_threshold){
		//发包到泵节点灌溉一次
		sCc9dPkg send_pkg;
		send_pkg.from_port = LOCAL_PORT;
		send_pkg.to_port = pumb_port;
		send_pkg.remote_addr = pumb_node_addr;
		send_pkg.datas[0] = pumb_cmd;
		send_pkg.datas[1] = pumb_run_seconds;
		cc9d_SendPkg(&send_pkg, 2);
		//记录现在的时间
		last_run_time = sec_ticks;
	}
}
static void setPumpArgument(sCc9dPkg* recv_pkg, uint8 data_len) {
	if (data_len != 6) {
		responePkg(recv_pkg, BAD_LENGTH);
		return;
	}
	#pragma pack(1) 
	struct {// 0007.32:/p w500 w10 |2
		uint8 cmd;	
		uint16 low_threshold;
		uint16 wait_time;
		uint8 pumb_run_seconds;
	}*arguments = (void*)(recv_pkg->datas);	//试试会不会非对齐访问
	#pragma pack()
	low_threshold = arguments->low_threshold;
	wait_time = arguments->wait_time;
	pumb_run_seconds = arguments->pumb_run_seconds;
	responePkg(recv_pkg, 0x00);
	printf("setPumpArgument low_threshold %d, wait_time %d, pumb_run_seconds %d\n", low_threshold, wait_time, pumb_run_seconds);
}

static void setPumpAddress(sCc9dPkg* recv_pkg, uint8 data_len) {
	if (data_len != 4) {
		responePkg(recv_pkg, BAD_LENGTH);
		return;
	}
	#pragma pack(1) 
	struct {// 0007.32:/a w1 |48 
		uint8 cmd;
		uint16 pumb_node_addr;
		uint8 pumb_port;
	}*arguments = (void*)(recv_pkg->datas);	//试试会不会非对齐访问
	#pragma pack()
	pumb_node_addr = arguments->pumb_node_addr;
	pumb_port = arguments->pumb_port;
	responePkg(recv_pkg, 0x00);
	printf("setPumpAddress pumb_node_addr %d, pumb_port %d\n", pumb_node_addr, pumb_port);
}


static void secTick(void) {
    static uint16 last_adc_value = 0x8000;//上一次的ADC值
    static uint16 timer_hart_beat = 0;//心跳包计时器
    static uint16 timer_store = 0;//储存计时器   
	static uint16 timer_pump_control = 0;
    timer_hart_beat++;
    timer_store++; 
	timer_pump_control++;
    if (sec_ticks % Adc_interval == 0) {
        uint16 adc_value = Adc_getValue(0);
        int32 diff = adc_value - last_adc_value;
        if ((diff > adc_report_threshold)
            || (diff < -adc_report_threshold)
            || (timer_hart_beat >= Hart_beat_interval)
            ) {
            sendAdcPkg(adc_value);
            last_adc_value = adc_value;
            timer_hart_beat = 0;
        }
        if (timer_store>=Store_interval){
            timer_store -= Store_interval;
            storeAdc(adc_value);
		}
		pumbControl(adc_value);
    }
}
void AdcApp_msTick(void) {
    static uint16 ms_ticks = 0;
    ms_ticks++;
    if (ms_ticks < 1000){
        return;
    } else{
        ms_ticks-=1000;
        sec_ticks++;
		secTick();
    }
}

void AdcApp_init(){
    Adc_init();
}


void AdcApp_recvPkg(sCc9dPkg* recv_pkg, uint8 data_len) {
	if (data_len == 0) {
		return;
	}
	uint8 cmd = recv_pkg->datas[0];
	switch (cmd) {
	case 'r': {
		uint16 adc_value = Adc_getValue(0);
		sCc9dPkg send_pkg;
		createResponsePkg(&send_pkg, recv_pkg);
		send_pkg.datas[0] = (adc_value >> 0) & 0xff;
		send_pkg.datas[1] = (adc_value >> 8) & 0xff;
		cc9d_SendPkg(&send_pkg, 2);
	}break;
	case 'c': {//获取最新记录的编号和时间
		sCc9dPkg send_pkg;
		createResponsePkg(&send_pkg, recv_pkg);
		*((uint32*)(send_pkg.datas)) = last_store_sec_ticks;
		*((uint16*)(send_pkg.datas + 4)) = record_sno;
		cc9d_SendPkg(&send_pkg, 6);
	}break;
	case 'h': {
		getHistory(recv_pkg, data_len);
	}break;
	case 't': {//获取最新记录的编号和时间
		sCc9dPkg send_pkg;
		createResponsePkg(&send_pkg, recv_pkg);
		*((uint32*)(send_pkg.datas)) = sec_ticks;
		cc9d_SendPkg(&send_pkg, 4);
	}break;
	case 'p': {//设置水泵的参数
		setPumpArgument(recv_pkg, data_len);
	}break;
	case 'a': {//设置水泵的地址
		setPumpAddress(recv_pkg, data_len);
	}break;
	case 'f': {//设置为快速模式
		setFastMode(recv_pkg, data_len);
	}break;

	default:
		break;
	}
}


